/********************************************************************************
 * @file    button.c
 * @brief   按键处理组件
 * 
 * @author  nzttj
 * @date    2025-02-18
 * @version 1.0
 * 
 * Copyright (C) 2025 nzttj
 * 
 * 本软件使用 GNU 通用公共许可证 (GPL) 第 3 版授权。
 * 
 * 您可以自由使用、修改、分发本软件，但必须遵守以下条件：
 * 1. 源代码必须开放，且您修改后的代码也必须以 GPL 许可证发布。
 * 2. 发布或分发本软件时，必须附带本许可证，并提供源代码或获取源代码的方式。
 * 
 * 本软件按“原样”提供，不附带任何担保，使用风险由用户自行承担。
 * 
 ********************************************************************************/

#include "button.h"
#include "driver/gpio.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include <stdlib.h>
#include <string.h>

#define BUTTON_TASK_STACK_SIZE (2048)
#define BUTTON_TASK_PRIORITY (4)
#define MAX_BUTTONS (8)         // 最大支持按键数
#define TASK_LOOP_DELAY_MS (10) // 任务循环间隔
#define TICKS_TO_MS(ticks) ((uint32_t)((ticks) * portTICK_PERIOD_MS))
#define TickTypeMax ((TickType_t)-1)

typedef struct
{
    button_config_t config;      // 按键配置
    button_event_cb_t cb;        // 事件回调
    void *cb_arg;                // 回调参数
    bool last_raw_state;         // 上次原始状态
    bool last_stable_state;      // 上次稳定状态
    TickType_t last_change_time; // 上次状态变化时间
    TickType_t press_time;       // 按下开始时间
    bool long_press_detected;    // 长按已触发标记
    TickType_t last_repeat_time; // 上次重复触发时间
} button_t;

static button_t *buttons[MAX_BUTTONS] = {0};
static TaskHandle_t button_task_handle = NULL;
static SemaphoreHandle_t xSemaphore = NULL;

/**
 * @brief 获取安全的时间差
 * @param newer 新时间戳
 * @param older 旧时间戳
 * @return 时间差（毫秒）
 */
static inline uint32_t _get_safe_duration(TickType_t newer, TickType_t older)
{
    return TICKS_TO_MS((newer >= older) ? (newer - older) : (TickTypeMax - older + newer));
}

/**
 * @brief 处理单个按键的状态检测与事件触发
 * @param btn 要处理的按键实例指针
 */
static void _process_button(button_t *btn)
{
    const TickType_t now = xTaskGetTickCount();
    bool current_state = gpio_get_level(btn->config.gpio_num);

    /*-------------------------------------
      步骤1：获取当前物理状态（消抖前）
    --------------------------------------*/
    // 根据 active_low 配置反转电平逻辑
    if (btn->config.active_low)
    {
        current_state = !current_state; // true=按下，false=释放
    }

    /*-------------------------------------
      步骤2：状态变化检测（消抖起点）
    --------------------------------------*/
    if (current_state != btn->last_raw_state)
    {
        btn->last_raw_state = current_state;
        btn->last_change_time = now; // 记录状态变化时间
    }

    /*-------------------------------------
      步骤3：消抖处理（状态稳定判定）
    --------------------------------------*/
    const uint32_t stable_duration = _get_safe_duration(now, btn->last_change_time);
    const bool is_stable = (stable_duration >= btn->config.debounce_time_ms);

    if (is_stable)
    {
        const bool state_changed = (current_state != btn->last_stable_state);
        if (state_changed)
        {
            btn->last_stable_state = current_state;

            /*-------------------------------------
              步骤4：触发边界事件（按下/释放）
            --------------------------------------*/
            if (current_state)
            {
                // 按下事件
                btn->press_time = now;
                btn->long_press_detected = false;
                btn->last_repeat_time = 0;
                if (btn->cb)
                {
                    btn->cb(BUTTON_EVENT_PRESSED, 0, btn->cb_arg);
                }
            }
            else
            {
                // 释放事件
                const uint32_t press_duration = _get_safe_duration(now, btn->press_time);
                if (btn->cb)
                {
                    // 触发顺序：先释放，后单击（避免状态混淆）
                    btn->cb(BUTTON_EVENT_RELEASED, press_duration, btn->cb_arg);
                    if (press_duration < btn->config.long_press_time_ms)
                    {
                        btn->cb(BUTTON_EVENT_SINGLE_CLICK, press_duration, btn->cb_arg);
                    }
                }
                // 重置长按相关状态
                btn->long_press_detected = false;
                btn->last_repeat_time = 0;
            }
        }
    }

    /*-------------------------------------
      步骤5：长按检测（持续按压状态）
    --------------------------------------*/
    if (btn->last_stable_state)
    { // 当前处于稳定按下状态
        const uint32_t press_duration = _get_safe_duration(now, btn->press_time);

        /*-- 子步骤5.1：首次长按触发 --*/
        if (!btn->long_press_detected && press_duration >= btn->config.long_press_time_ms)
        {
            btn->long_press_detected = true;
            btn->last_repeat_time = now; // 记录首次触发时间
            if (btn->cb)
            {
                btn->cb(BUTTON_EVENT_LONG_PRESSED, press_duration, btn->cb_arg);
            }
        }

        /*-- 子步骤5.2：长按重复触发 --*/
        if (btn->long_press_detected && btn->config.repeat_long_press)
        {
            const uint32_t elapsed_since_last_repeat = _get_safe_duration(now, btn->last_repeat_time);

            if (elapsed_since_last_repeat >= btn->config.repeat_interval_ms)
            {
                btn->last_repeat_time = now; // 更新最后触发时间
                if (btn->cb)
                {
                    btn->cb(BUTTON_EVENT_LONG_PRESSED, press_duration, btn->cb_arg);
                }
            }
        }
    }
}

/**
 * @brief 按键处理任务，周期性检测所有按键状态
 */
static void button_task(void *arg)
{
    TickType_t last_wake_time = xTaskGetTickCount();

    for (;;)
    {
        if (xSemaphoreTake(xSemaphore, portMAX_DELAY) == pdTRUE)
        {
            button_t *local_buttons[MAX_BUTTONS];
            memcpy(local_buttons, buttons, sizeof(buttons));
            xSemaphoreGive(xSemaphore);

            for (int i = 0; i < MAX_BUTTONS; i++)
            {
                if (local_buttons[i])
                {
                    _process_button(local_buttons[i]);
                }
            }
        }
        vTaskDelayUntil(&last_wake_time, pdMS_TO_TICKS(TASK_LOOP_DELAY_MS));
    }
}

button_handle_t button_init(const button_config_t *config)
{
    if ((!config) || (!xSemaphore))
    {
        return NULL;
    }

    button_t *btn = calloc(1, sizeof(button_t));
    if (!btn)
    {
        return NULL;
    }

    memcpy(&btn->config, config, sizeof(button_config_t));

    // 配置GPIO
    gpio_config_t io_conf = {
        .pin_bit_mask = (1ULL << config->gpio_num),
        .mode = GPIO_MODE_INPUT,
        .pull_up_en = config->pull_up_en,
        .pull_down_en = config->pull_down_en,
        .intr_type = GPIO_INTR_DISABLE
    };
    if (gpio_config(&io_conf) != ESP_OK)
    {
        free(btn);
        return NULL;
    }

    bool added = false;
    // 添加到按钮数组
    xSemaphoreTake(xSemaphore, portMAX_DELAY);
    for (int i = 0; i < MAX_BUTTONS; i++)
    {
        if (!buttons[i])
        {
            buttons[i] = btn;
            added = true;
            break;
        }
    }
    xSemaphoreGive(xSemaphore);

    if (!added) // 数组已满，释放内存
    {
        free(btn);
        return NULL;
    }

    return (button_handle_t)btn;
}

void button_register_cb(button_handle_t handle, button_event_cb_t cb, void *arg)
{
    button_t *btn = (button_t *)handle;
    if (btn)
    {
        btn->cb = cb;
        btn->cb_arg = arg;
    }
}

void button_deinit(button_handle_t handle)
{
    if ((!handle) || (!xSemaphore))
    {
        return;
    }

    xSemaphoreTake(xSemaphore, portMAX_DELAY);
    for (int i = 0; i < MAX_BUTTONS; i++)
    {
        if (buttons[i] == handle)
        {
            buttons[i] = NULL;
            break;
        }
    }
    xSemaphoreGive(xSemaphore);

    free(handle);
}

int button_system_init(void)
{
    if (!xSemaphore)
    {
        xSemaphore = xSemaphoreCreateMutex();
        if (!xSemaphore)
        {
            return -1;
        }
    }

    if (!button_task_handle)
    {
        if (xTaskCreate(button_task, "button_task", BUTTON_TASK_STACK_SIZE, NULL, BUTTON_TASK_PRIORITY, &button_task_handle) != pdPASS)
        {
            vSemaphoreDelete(xSemaphore);
            xSemaphore = NULL;
            return -2;
        }
    }

    return 0;
}

int button_system_deinit(void)
{
    if (button_task_handle)
    {
        vTaskDelete(button_task_handle);
        button_task_handle = NULL;
    }

    if (xSemaphore)
    {
        vSemaphoreDelete(xSemaphore);
        xSemaphore = NULL;
    }

    return 0;
}
